﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Collections.Concurrent;

namespace Jacobi.ComponentModel.Decomposition
{
    /// <summary>
    /// A Queue implementation that blocks readers when no data is available.
    /// </summary>
    /// <typeparam name="T">The data type of the items in the queue.</typeparam>
    internal class BlockingQueue<T>
    {
        private ConcurrentQueue<T> _queue = new ConcurrentQueue<T>();
        private ManualResetEventSlim _event = new ManualResetEventSlim(false);

        /// <summary>
        /// Puts a new data item on the queue.
        /// </summary>
        /// <param name="data">The data item.</param>
        public void Enqueue(T data)
        {
            _queue.Enqueue(data);
            ReleaseLock();
        }

        /// <summary>
        /// Removes a data item from the queue.
        /// </summary>
        /// <returns>Returns the new data item.</returns>
        /// <remarks>The calling Thread is blocked until data is available.</remarks>
        public T Dequeue()
        {
            WaitForLock();

            T value = default(T);
            _queue.TryDequeue(out value);

            return value;
        }

        /// <summary>
        /// Gets an indication whether a call to <see cref="Dequeue"/> will block the calling Thread.
        /// </summary>
        public bool WillBlock
        {
            get { return (_queue.Count == 0); }
        }

        private void ReleaseLock()
        {
            _event.Set();
        }

        private void WaitForLock()
        {
            if (WillBlock)
            {
                _event.Reset();
            }

            _event.Wait();
        }
    }
}
